import json
import copy
import math
from copy import deepcopy
from trace import Trace

from common_util import partial_deepcopy, save_recipes_pretty

config = {
    "配方数据路径": "dsp_data.json",
    "制造等级": 3,
    "化工等级": 2,
    "熔炉等级": 2,
    "传送带等级": 3,
    "分拣器等级": 4,
    "采矿机模式": "3", # 1: 纯采矿机采矿; 2: 纯大型采矿机采矿； 3: 采矿机 + 大型采矿机混合采矿; 4: 采矿机 + 大型采矿机最大倍数加力采矿
    "精炼等级": 1,
    "科研等级": 1,
    "对撞机等级": 1,
}

meta_data = {
    "制造速度" : {
        1 : 0.75,
        2 : 1,
        3 : 1.5,
        4: 3
    },
    "化工速度" : {
        1 : 1,
        2 : 2
    },
    "熔炉速度": {
        1: 1,
        2: 2,
        3: 3
    },
    "传送带速度": {
        1: 6,
        2: 12,
        3: 30
    },
    "采矿机速度": {
        1: 0.5, # 普通采矿机
        2: 1, # 大型采矿机
        3: 3 # 大型采矿机加力至3倍采矿
    },
    "抽水站速度": {
        1: 5/6,
    },
    "分拣器速度": {
        1 : 1.5,
        2: 3,
        3: 6,
        4: 10
    },
    "精炼速度": {
        1: 1
    },
    "科研速度": {
        1: 1
    },
    "对撞机速度": {
        1: 1
    },
    "增产量": {
        0: 0,
        1: 0.125,
        2: 0.2,
        3: 0.25
    },
    "增产剂加速": {
        0: 0,
        1: 0.25,
        2: 0.5,
        3: 1
    }
}

source_machine = [
    "采矿机", "轨道采集器", "抽水站", "射线接收站", "原油萃取站", "黑雾残骸"
]

source_from = [
    "矿脉", "巨行星","气巨星","冰巨星", "海洋", "原油涌泉", "戴森云", "戴森球"
]

all_goods_list = [
"电力感应塔", "无线输电塔", "卫星配电站", "风力涡轮机", "火力发电厂", "太阳能板", "地热发电站", "微型聚变发电站", "能量枢纽", "蓄电器", "蓄电器（满）", "射线接收站", "人造恒星", "传送带", "高速传送带", "极速传送带", "四向分流器", "自动集装机", "流速监测器", "喷涂机", "小型储物仓", "大型储物仓", "储液罐", "物流配送器", "行星内物流运输站", "星际物流运输站", "轨道采集器", "分拣器", "高速分拣器", "极速分拣器", "集装分拣器", "采矿机", "大型采矿机", "抽水站", "原油萃取站", "原油精炼厂", "分馏塔", "化工", "量子化工", "微型粒子对撞机", "熔炉", "位面熔炉", "负熵熔炉", "制造MK.I", "制造MK.II", "制造MK.III", "重组式制造", "矩阵研究站", "自演化研究站", "电磁轨道弹射器", "垂直发射井", "高斯机枪塔", "导弹防御塔", "聚爆加农炮", "高频激光塔", "磁化电浆炮", "近程电浆塔", "战场分析基站", "干扰塔", "信号塔", "行星护盾发生器", "铁矿", "铜矿", "石矿", "煤矿", "硅石", "钛石", "水", "原油", "氢", "重氢", "反物质", "核心素", "临界光子", "金伯利矿石", "铁块", "铜块", "石材", "高能石墨", "高纯硅块", "钛块", "硫酸", "精炼油", "液氢燃料棒", "氘核燃料棒", "反物质燃料棒", "奇异湮灭燃料棒", "导弹组", "分形硅石", "磁铁", "磁线圈", "玻璃", "金刚石", "晶格硅", "钛合金", "燃烧单元", "塑料", "有机晶体", "石墨烯", "湮灭约束球", "机枪弹箱", "超音速导弹组", "光栅石", "钢材", "电路板", "棱镜", "电动机", "微晶元件", "增产剂Mk.I", "爆破单元", "奇异物质", "钛晶石", "碳纳米管", "粒子宽带", "钛化弹箱", "引力导弹组", "刺笋结晶", "齿轮", "电浆激发器", "光子合并器", "电磁涡轮", "处理器", "增产剂MK.II", "晶石爆破单元", "卡西米尔晶体", "钛化玻璃", "位面过滤器", "量子芯片", "超合金弹箱", "炮弹组", "单极磁石", "动力引擎", "推进器", "加力推进器", "超级磁场环", "粒子容器", "增产剂MK.III", "原型机", "精准无人机", "攻击无人机", "护卫舰", "驱逐舰", "等离子胶囊", "高爆炮弹组", "可燃冰", "配送运输机", "物流运输机", "星际物流运输船", "空间翘曲器", "引力透镜", "地基", "太阳帆", "框架材料", "戴森球组件", "小型运载火箭", "反物质胶囊", "晶石炮弹组", "木材", "电磁矩阵", "能量矩阵", "结构矩阵", "信息矩阵", "引力矩阵", "宇宙矩阵", "黑雾矩阵", "能量碎片", "硅基神经元", "负熵奇点", "物质重组器", "干扰胶囊", "压制胶囊", "植物燃料"
]

only_speed_up_goods_list = set(["反物质燃料棒", "反物质", "无线输电塔", "卫星配电站", "高速传送带", "急速传送带", "星际物流运输站", "轨道采集器", "高速分拣器", "急速分拣器", "集装分拣器", "量子化工厂", "位面熔炉", "制造台Mk.II", "制造台Mk.III", "信号塔"])

def is_source(machine_type):
    for elem in source_from:
        if elem in machine_type:
            return True
    return False

def get_speed_info():
    res = {
        "制造" : meta_data["制造速度"][config["制造等级"]],
        "化工": meta_data["化工速度"][config["化工等级"]],
        "熔炉": meta_data["熔炉速度"][config["熔炉等级"]],
        "传送带": meta_data["传送带速度"][config["传送带等级"]],
        "精炼": meta_data["精炼速度"][config["精炼等级"]],
        "采矿机模式": config["采矿机模式"],
        "科研": meta_data["科研速度"][config["科研等级"]],
        "对撞": meta_data["对撞机速度"][config["对撞机等级"]],
    }
    return res

def read_json(f_path):
    try:
        with open(f_path, 'r', encoding='utf-8') as file:
            res = json.load(file)
            return res
    except FileNotFoundError:
        print(f"文件 {f_path} 不存在")
        return []
    except json.JSONDecodeError:
        print("JSON 文件格式错误")
        return []

def to_map(goods_list):
    res = {}
    for elem in goods_list:
        if not elem:
            continue
        name = elem["name"]
        if name not in res:
            res[name] = []
        res[name].append(elem)
    return res

def get_needs_name_arr(arr):
    res = []
    for elem in arr:
        res.append(elem["name"])
    return res

def compare_needs_arr(arr1, arr2):
    arr1_sorted = sorted(arr1)
    arr2_sorted = sorted(arr2)
    return arr1_sorted == arr2_sorted

def compare_needs(need1, arr0):
    if not arr0:
        return False
    return compare_needs_arr(get_needs_name_arr(need1), arr0)


def choose_goods(goods_arr, needs_arr):
# 如果存在指定配方，则使用指定的
    if needs_arr:
        for elem in goods_arr:
            if compare_needs(elem["needs"], needs_arr):
                return elem
# 否则，选择需要最少的配方
    idx = 0
    count = len(goods_arr[0]["needs"])
    for elem in goods_arr:
        if len(elem["needs"]) < count:
            idx = goods_arr.index(elem)
            count = len(elem["needs"])
    return goods_arr[idx]


def calc_tree_list(name, num_per_sec, goods_map,speed_info, level, origin_map={}, do_pentu=False, pentu_map={}, stop_set={}):
    if goods_map[name] is None or not goods_map[name]:
        raise f"{name} 未找到。更改配置表或者入参名"
    goods_arr = deepcopy(goods_map[name])
    goods = choose_goods(goods_arr, origin_map.get(name, []))
    goods["level"] = level
    goods["num_per_s"] = num_per_sec
    pentu_level = 0
    if do_pentu:
        pentu_level = pentu_map.get(name, {}).get("pentu", 0)
    goods["pentu_level"] = pentu_level
    increase_rate = 1 + meta_data["增产量"].get(pentu_level, 0)
    speed_up_rate = 1
    if name in only_speed_up_goods_list:
        increase_rate = 1
        speed_up_rate = speed_up_rate + meta_data["增产剂加速"].get(pentu_level, 0)
    if is_source(goods["machine"]):
        goods["machine_num"] = f"需求【{goods['name']}: {goods['num_per_s']}】, 根据矿产情况计算采集器械数."
    else:
        speed = speed_info[goods["machine"]]
        goods["machine_num"] = num_per_sec / (goods["mount"] / goods["time"]) / speed/ increase_rate / speed_up_rate
    if name in stop_set:
        return goods
    needs = deepcopy(goods["needs"])
    needs_info = []
    needs_count = 1
    for need in needs:
        need_num = (num_per_sec / goods["mount"]) * need["mount"] / increase_rate
        need_info = calc_tree_list(need["name"], need_num, goods_map, speed_info, f"{level}.{needs_count}", origin_map, do_pentu, pentu_map, stop_set)
        needs_info.append(need_info)
        needs_count += 1
    goods["needs"] = needs_info
    return goods


def trans_to_full_list(tree_list):
    map_res = {}
    iter_trans_full_list(tree_list, map_res)
    list_res = []
    for elem in map_res:
        list_res.append(map_res[elem])
    sorted_res = sort_group_by_level(list_res)
    return sorted_res

def iter_trans_full_list(tree_node, res):
    name = tree_node["name"]
    if name in res:
        res[name]["num_per_s"] = res[name]["num_per_s"] + tree_node["num_per_s"]
        if is_source(tree_node["machine"]):
            res[name]["machine_num"] = f"需求【{res[name]["name"]}: {res[name]['num_per_s']}】, 根据矿产情况计算采集器械数."
        else:
            res[name]["machine_num"] = res[name]["machine_num"] + tree_node["machine_num"]
    else:
        cur = {}
        partial_deepcopy(tree_node, ["name", "time", "mount", "machine", "num_per_s", "level", "machine_num"], cur)
        res[name] = cur
    for elem in tree_node["needs"]:
        iter_trans_full_list(elem, res)
    return res


def calc_dsp(name, num_per_sec, res_type = "tree_list", origin_map={}, do_pentu=True, pentu_def={}, stop_set=set()):
    """
    计算DSP的生产树
    :param name: 商品名称
    :param num_per_sec: 每秒生产数量
    :param res_type: 结果类型，"tree_list" 或 "full_list"
    :param origin_map: 指明产物原料，变更产线路线
    :param do_pentu: 是否进行喷涂
    :param pentu_def: 自定义喷涂map ，key为物品名称，value为喷涂级别, 0为不喷涂, 1为一级喷涂, 2为二级喷涂, 3为三级喷涂
    :param stop_set: 指明递归截止物品位置
    :return: 生产树或生产列表
    """
    pentu_map = read_json("./data/pentu_map.json")
    for elem in pentu_def:
        pentu_map[elem] = {
            "name": elem,
            "pentu": pentu_def[elem]
        }
    goods_list = read_json("./data/receipts_html/0_全集结果.json")
    goods_map = to_map(goods_list)
    tree_list = calc_tree_list(name, num_per_sec, goods_map, get_speed_info(), "1", origin_map, do_pentu, pentu_map, stop_set)
    if res_type == "tree_list":
        return tree_list
    elif res_type == "full_list":
        res = trans_to_full_list(tree_list)
        return res
    return tree_list

def print_tree_list(tree_list):
    pretty_json = json.dumps(tree_list, indent=4, ensure_ascii=False)
    print(pretty_json)

import re
from typing import List, Tuple

def sort_group_by_level(items: List[dict], strict: bool = False) -> List[dict]:
    """
    先按层级（len(parts)）升序，再按每一段的整数值逐段比较。
    strict=True 时遇到非数字组件会抛错；默认会尝试从组件中提取数字（没有则视为0）。
    """
    def parse_key(dk: dict) -> Tuple[int, Tuple[int, ...]]:
        s = dk["level"]
        s = s.strip()
        if s == "":
            return 0, ()
        parts = s.split('.')
        nums = []
        for p in parts:
            if p.isdigit():
                nums.append(int(p))
            else:
                m = re.search(r'\d+', p)
                if m:
                    nums.append(int(m.group(0)))
                else:
                    if strict:
                        raise ValueError(f"Non-numeric component '{p}' in '{s}'")
                    nums.append(0)
        return len(nums), tuple(nums)

    return sorted(items, key=parse_key)


if __name__ == '__main__':
    # tree_list full_list
    res_type = "tree_list"
    name = "反物质燃料棒"
    do_pentu = True
    pentu_def = {}
    res = calc_dsp(name, 6, res_type, {
        "卡西米尔晶体": ["光栅石", "石墨烯", "氢"],
        "光子合并器": ["光栅石", "电路板"],
        "金刚石": ["金伯利矿石"],
        "粒子容器": ["单极磁石", "铜块"],
        "晶格硅": ["分形硅石"]
    },do_pentu, pentu_def, {

    })

    print_tree_list(res)
    save_recipes_pretty(res, f"./output2/{name}_{res_type}.json")

